//
// Created by root on 6/18/17.
//

#include <stdlib.h>
#include "runner_fa.h"

static void test_alloc_transit_node() {
    TransitNode *node = alloc_transit_node();
    assert_non_null(node);
}

static State * goto_next(State *state) {
    HashTable stateTable = getStateTable();
    HashNode *node = (HashNode *) get_table_element(stateTable, state, hash_code_state);
    return ((State *) (((TransitNode *)(node->element))->next));
}

static State * exit_loop(State *state) {
    HashTable stateTable = getStateTable();
    HashNode *node = (HashNode *) get_table_element(stateTable, state, hash_code_state);
    return ((State *) (((TransitNode *)(node->next->element))->next));
}

static void test_to_dfa() {
    assert_null(to_dfa(NULL));

    // {define function {add}: {add {a} {b}} as {{a} + {b}}}
    State *state1 = to_dfa(create_statement());
    // {for the conditions: [when {a} then {b} ,]}
    State *state2 = to_dfa(create_loop_statement());
    // {let [{a}={b},] eval: [{c}]}
    State *state3 = to_dfa(create_loop_statement1());

    State *state = state1;          //[start]
    assert_false(state->accept);
    state = goto_next(state);       //define
    assert_false(state->accept);
    state = goto_next(state);       //function
    assert_false(state->accept);
    state = goto_next(state);       //{add}
    assert_false(state->accept);
    state = goto_next(state);       //:
    assert_false(state->accept);
    state = goto_next(state);       //{add {a} {b}}
    assert_false(state->accept);
    state = goto_next(state);       //as
    assert_false(state->accept);
    state = goto_next(state);       //{{a} + {b}}
    assert_true(state->accept);

    state = state2;                 //[start]
    assert_false(state->accept);
    state = goto_next(state);       //for
    assert_false(state->accept);
    state = goto_next(state);       //the
    assert_false(state->accept);
    state = goto_next(state);       //conditions:
    assert_true(state->accept);

    state = goto_next(state);       //when
    assert_false(state->accept);
    state = goto_next(state);       //{a}
    assert_false(state->accept);
    state = goto_next(state);       //then
    assert_false(state->accept);
    state = goto_next(state);       //{b}
    assert_false(state->accept);
    state = goto_next(state);       //,
    assert_true(state->accept);

    state = goto_next(state);       //when
    assert_false(state->accept);
    state = goto_next(state);       //{a}
    assert_false(state->accept);
    state = goto_next(state);       //then
    assert_false(state->accept);
    state = goto_next(state);       //{b}
    assert_false(state->accept);
    state = goto_next(state);       //,
    assert_true(state->accept);

    state = state3;                 //[start]
    assert_false(state->accept);
    state = goto_next(state);       //let
    assert_false(state->accept);
    state = goto_next(state);       //{a}
    assert_false(state->accept);
    state = goto_next(state);       //=
    assert_false(state->accept);
    state = goto_next(state);       //{b}
    assert_false(state->accept);
    state = goto_next(state);       //,
    assert_false(state->accept);
    state = goto_next(state);       //{a}
    assert_false(state->accept);
    state = goto_next(state);       //=
    assert_false(state->accept);
    state = goto_next(state);       //{b}
    assert_false(state->accept);
    state = goto_next(state);       //,
    assert_false(state->accept);
    state = exit_loop(state);       //eval:
    assert_true(state->accept);
    state = goto_next(state);       //{c}
    assert_true(state->accept);
    state = goto_next(state);       //{c}
    assert_true(state->accept);
}

static void test_transit() {
    // {{x} or {y}}
    State *starter = to_dfa(create_source_statement1());
    Statement *s1 = create_source_statement1();
    Statement *s2 = create_source_statement1();
    State *state = starter;
    Queue *repQueue = init_queue();

    state = transit(starter, state, s1, repQueue);
    state = transit(starter, state, init_sym("or"), repQueue);
    state = transit(starter, state, s2, repQueue);
    assert_true(state->accept);
    KeyValueNode *node1 = get_queue_element(repQueue, 0);
    KeyValueNode *node2 = get_queue_element(repQueue, 1);

    assert_string_equal("x", node1->key);
    assert_string_equal("y", node2->key);
    assert_ptr_equal(node1->value, s1);
    assert_ptr_equal(node2->value, s2);
}

static void test_get_transit_node_from_table() {
    // {let [{a}={b},] eval: [{c}]}
    State *state1 = to_dfa(create_loop_statement1());
    State *state = state1;
    TransitNode *node = NULL;
    node = get_transit_node_from_table(state1, state, init_sym("let"));
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_arg());
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_sym("="));
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_arg());
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_sym(","));
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_arg());
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_sym("="));
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_arg());
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_sym(","));
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_sym("eval:"));
    assert_non_null(node);
    state = exit_loop(state);
    node = get_transit_node_from_table(state1, state, init_arg());
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_arg());
    assert_non_null(node);
    state = goto_next(state);
    node = get_transit_node_from_table(state1, state, init_arg());
    assert_non_null(node);
}

int main(int argc, char **argv) {
    const struct CMUnitTest tests[] = {
            cmocka_unit_test(test_alloc_transit_node),
            cmocka_unit_test(test_to_dfa),
            cmocka_unit_test(test_transit),
            cmocka_unit_test(test_get_transit_node_from_table),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}
